package mpiserver;
import Network.s2sInnerLogic;
import baseservergrid.ModeCompilation;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import javax.swing.Timer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import repository.IRepository;
import repository.exception.NoRepositoryException;
import repository.exception.NullRootException;
import repository.exception.RepositoryException;
import taskmanager.JTask;
/**
 *
 * @author Алексей ID=2001
 */
public class MpiServer implements IMpiServer {

    IRepository repository=null;

    //путь до директории со скриптами
    private String scriptsPath="";
    //путь до настроичного файла
    private String settingsPath="";
    //путь до выходного файла smpd -printprocs
    private String smpdProcsLogPath="";
    //путь до выходного файа проверки работы smpd
    private String smpdTestLogPath="";

    //строка вызова скрипта для запуска mpi
    private String scrMpiRun="";
    //строка вызова скрипта для запуска gcc
    private String scrGccRun="";
    //строка вызова скрипта для запуска smpd
    private String scrSmpdStart="";
    //строка вызова скрипта для останова smpd
    private String scrSmpdKill="";
    //строка вызова скрипта для проверки работы smpd
    private String scrSmpdTest="";

    //квота на размер выходного файла smpd -printprocs
    //private long quote = 5000000;

    /** обработчики событий ведомых серверов */
    private Vector<MpiListener> listeners;
    /** таймер сканирования задач */
    private Timer timer=null;
    /** интервал между опросами */
    private int delay = 10000;
    /** статус разбора лог файла. true - идет разбор */
    private boolean isParsing=false;
    /** величина задержки */
    private int taskDelay = 5000;
    /** таймер задержки между задачами */
    private Timer taskTimer=null;
    /** вемя запуска последней задачи */
    private long lastTimeRun=0;

    /**
     * Класс для сканирования smpd log файла
     */
    private class SmpdCheck implements ActionListener{
        /**
         * Обработка событий таймера
         * @param e событие
         */
        public void actionPerformed(ActionEvent e) {
            if(!isParsing){ parseSmpdLog(); startSmpd();}
        }

    }

    /**
     * Класс для задержки между задачами
     */
    private class WaitTask implements ActionListener{
        /**
         * Обработка событий таймера
         * @param e событие
         */
        public void actionPerformed(ActionEvent e) {
            taskTimer.stop();
        }
    }

    /**
     * Конструктор
     */
    public MpiServer(IRepository repository){
        this.repository=repository;
        listeners=new Vector<MpiListener>();
        settingsPath="."+File.separator+"mpiconfig"+File.separator+"config.ini";
        //создать таймер для разбора лог файла
        timer=new Timer(delay,new SmpdCheck());
    }
    /**
     * Конструктор
     * @param path путь до настроичного файла
     */
    public MpiServer(String path,IRepository repository){
        this.repository=repository;
        settingsPath=path;
        //создать таймер для разбора лог файла
        timer=new Timer(delay,new SmpdCheck());
    }

    /**
     * Разобрать выходной файл smpd -printpocs
     * определение факта завершения работы задачи
     */
    private void parseSmpdLog(){
        isParsing=true;
        try{
            BufferedReader in = new BufferedReader(new FileReader(smpdProcsLogPath));
            try{
                System.out.println("SMPD-------------------------------");
				while(in.ready() == true){
                    String tmp = in.readLine();
                    System.out.println(tmp);
                }
            }
            finally{
                in.close();
            }
        }
        catch(Exception e){}
        isParsing=false;
    }

    /**
     * Запросить путь до настроичного файла
     * @return путь до настроичного файла
     */
    public String getSettingsPath (){
        return settingsPath;
    }
    
    /**
     * Установить путь до настроичного файла
     * @param settingsPath путь до настроичного файла
     */
    public void setSettingsPath(String settingsPath){
        this.settingsPath=settingsPath;
    }

    /**
     * Запросить путь до директории со скриптами
     * @return путь до директории со скриптами
     */
    public String getScriptsPath(){
        return scriptsPath;
    }

    /**
     * Установить путь до директории со скриптами
     * @param scriptsPath путь до директории со скриптами
     */
    public void setScriptsPath(String scriptsPath){
        this.scriptsPath = scriptsPath;
    }

    /**
     * Прочитать строку скрипта и заменить статическае свойства
     * @param iniReader парсер файла настроек
     * @param scrName имя свойства строки вызова скрипта
     * @param scrVars имена свойтв статических переменных
     * @return строка вызова скрипта
     * @throws java.lang.Exception ошибка при чтении свойств
     */
    private String readScr(Settings iniReader,String scrName,String... scrVars)
            throws Exception{
        //прочитать строку скрипта
        String tmpStr=iniReader.getField(scrName);
        //заменить статические переменные
        for(String varStr:scrVars)
            tmpStr=tmpStr.replace(varStr, iniReader.getField(varStr));
        return tmpStr;
    }

    /**
     * Сгенерировать событие о завершении задачи
     * @param task задача
     */
    private void genEndingEvent(JTask task){
        //создать событие
        MpiEvent event=new MpiEvent(this,task.getTaskID(),s2sInnerLogic.getGUID());
        //отправить слушателям
        for(MpiListener l:listeners) l.endingTask(event);
    }

    /**
     * Сгенерировать событие об ошибке запуска задачи
     * @param task задача
     */
    private void genErrorEvent(JTask task){
        //создать событие
        MpiEvent event=new MpiEvent(this,task.getTaskID(),s2sInnerLogic.getGUID());
        //отправить слушателям
        for(MpiListener l:listeners) l.errorTask(event);
    }  

    /**
     * Прочитать командные строки вызова скриптов
     * @return true в случае успеха, иначе false
     */
    @Override
    public boolean config() {
        return setSettings(getSettings());
    }

    /**
     * Запросить парсер с загруженными настройками
     * @return парсер с настройками
     */
    @Override
    public Settings getSettings(){
        //получить файл настроек
        Settings iniReader=new Settings(settingsPath);
        iniReader.loadFile();
        return iniReader;
    }

    /**
     * Установить настройки
     * @param iniReader парсер с загруженными настройками
     * @return true в случае успеха
     */
    @Override
    public boolean setSettings(Settings iniReader){
        //считать скрипты
        try {
            //считать скрипт запуска mpi
            scrMpiRun=readScr(iniReader,"mpi_run","@SCRDIR@","@MPI@");
            //считать скрипт запуска gcc
            scrGccRun=readScr(iniReader,"gcc_run","@SCRDIR@","@GCC@","@INCLUDE@","@LIB@");
            //считать скрипт запуска smpd
            scrSmpdStart=readScr(iniReader,"smpd_start","@SCRDIR@","@SMPD@");
            //считать скрипт останова smpd
            scrSmpdKill=readScr(iniReader,"smpd_kill","@SCRDIR@");
            //считать скрипт проверки smpd
            scrSmpdTest=readScr(iniReader,"smpd_test","@SCRDIR@");
            //запросить директрию скриптов
            scriptsPath=iniReader.getField("@SCRDIR@");
            //запросить квоту
            //quote=Long.parseLong(iniReader.getField("ppfile_quote"));
            smpdProcsLogPath=scriptsPath+File.separator+"smpdprocs_log.txt";
            smpdTestLogPath=scriptsPath+File.separator+"smpdtest_log.txt";
        } catch (Exception ex) {
            return false;
        }
        return true;
    }

    /**
     * Начать сканирование состояния исполнения задач
     */
    public void startTaskScaning(){timer.start();}

    /**
     * Завершить сканирования состояния исполнения задач
     */
    public void stopTaskScaning(){timer.stop();}

    /**
     * Остановить smpd
     * @throws java.io.IOException ошибка при вызове командной строки
     */
//    @Override
//    public boolean killSmpd()/* throws IOException*/{
//        try {
//            Runtime.getRuntime().exec(scrSmpdKill);
//            return true;
//        } catch (IOException ex) {
//            return false;
//        }
//    }
    /**
     * Запустить smpd -printprocs
     * @throws java.io.IOException ошибка при вызове командной строки
     */
    @Override
    public boolean startSmpd() /*throws IOException*/{
        String tmpStr=scrSmpdStart.replace("%SLOG%",smpdProcsLogPath);
        try {
            if(ModeCompilation.OSENABLE)
                Runtime.getRuntime().exec(tmpStr);
            return true;
        } catch (IOException ex) {
            return false;
        }
    }

    /**
     * Проверка работы smpd
     * @return true - smpd запущен, иначе false
     * @throws java.io.IOException ошибка при вызове командной строки
     */
//    @Override
//    public boolean testSmpd() throws IOException{
//        File f = new File(smpdTestLogPath);
//        boolean res=false;
//        //удалить старый log
//        if(f.exists())
//            if(!f.delete()) return false;
//        //вызвать скрипт проверки
//        String tmpStr=scrSmpdStart.replace("%TLOG%","\""+smpdTestLogPath+"\"");
//        Runtime.getRuntime().exec(tmpStr);
//        //проверка log файла
//        BufferedReader in = new BufferedReader(new FileReader(smpdTestLogPath));
//        try{
//            while(in.ready() == true){
//                tmpStr=in.readLine();
//                res=tmpStr.contains("Console");
//                if(res) break;
//            }
//        }
//        finally{in.close();}
//        return res;
//    }

    /**
     * Проверка
     * @return true - если лог-файл SMPD-сервера был
     * обновлён из-за превышения заданной квоты.
     * false - если файл не обновлялся, и продолжает удовлетворять квоте
     * @throws java.io.IOException ошибка при вызове командной строки
     */
    /*@Override
    public boolean checkSMPDLogFile() throws IOException {
        File f = new File(smpdProcsLogPath);
        //проверить квоту
        if (f.length() > quote){
            killSmpd();
            f.delete();
            startSmpd();
            return true;
        }
        return false;
    }*/

    /**
     * Отправить задачу на выполнение в mpi
     * @param task задача
     * @param procsNum количество процессов
     * @return false - файла не существует
     * @throws mpiserver.MpiException ошибка при вызове командной строки
     * @throws repository.exception.RepositoryException ошибка репозитория
     */
    public void runMpi(JTask task/*, int procsNum*/)
            /*throws MpiException, RepositoryException*/{
        if(task==null) throw new NullPointerException();
        String tmpStr=scrMpiRun;
        try{
            //проверить наличие исполняемого файла
            File f=repository.getFile(task.getUserID(), task.getExecFile());
            if(!f.exists()){
                task.setStatus(JTask.ERROR); //установить статус ошибки
                genErrorEvent(task);
                return;
            }
            //подставить параметры в вызов командной строки
            tmpStr=tmpStr.replace("%WDIR%",
                    f.getParentFile().getAbsolutePath());
            tmpStr=tmpStr.replace("%NPROC%", String.valueOf(task.getUserID()));
            tmpStr=tmpStr.replace("%NPROC%", String.valueOf(task.getProcNum()));
            tmpStr=tmpStr.replace("%MEXEC%",f.getPath());
            tmpStr=tmpStr.replace("%MLOG%",repository.getFilePath(task.getUserID(),
                    task.getLogExecFile()));
            //запустить выполнение скрипта
            if(ModeCompilation.OSENABLE)
                Runtime.getRuntime().exec(tmpStr);
            task.setStatus(JTask.ACTIVE); //установить статус выполнения
            
            //
            //  УБРАТЬ ЗАГЛУШКУ !!!
            //
            //сгенерировать событие о завершении задачи
            task.setStatus(JTask.ENDED);  // << заглушка
            genEndingEvent(task);  // << заглушка
        } catch (Exception ex) { //ошибка вызова командной строки
            task.setStatus(JTask.ERROR); //установить статус ошибки
            genErrorEvent(task);
            //throw new MpiException();
        }
        //return true;
    }

    /**
     * Отправить задачу на компиляцию
     * @param task задача
     * @return false - файла исходников не существует
     * @throws java.io.IOException ошибка при вызове командной строки
     * @throws NoRepositoryException нет репозитория у пользователя
     * @throws NullRootException не задана директория репозитория
     */
    @Override
    public void runGcc(JTask task)
            /*throws IOException,NoRepositoryException,NullRootException*/{
        if(task==null) throw new NullPointerException();
        try{
            //проверить наличие исходного файла
            File f=repository.getFile(task.getUserID(), task.getSourceFile());
            if(!f.exists()){
                task.setStatus(JTask.ERROR); //установить статус ошибки
                genErrorEvent(task);
                return;
            }
            //подставить параметры в вызов командной строки
            String tmpStr=scrGccRun.replace("%SOURCE%",f.getPath());
            tmpStr=tmpStr.replace("%GEXEC%",repository.getFilePath(task.getUserID(),
                    task.getExecFile()));
            tmpStr=tmpStr.replace("%GLOG%",repository.getFilePath(task.getUserID(),
                    task.getLogSourceFile()));
            if(ModeCompilation.OSENABLE)
                Runtime.getRuntime().exec(tmpStr);
            //
            //  УБРАТЬ ЗАГЛУШКУ !!!
            //
            //сгенерировать событие о завершении задачи
            task.setStatus(JTask.ENDED);  // << заглушка
            genEndingEvent(task);  // << заглушка
        }
        catch (Exception ex) { //ошибка вызова командной строки
            task.setStatus(JTask.ERROR); //установить статус ошибки
            genErrorEvent(task);
            //throw new MpiException();
        }
        //return true;
    }

    /**
     * Добавить обработчик событий ведомого сервера
     * @param listener обработчик события
     */
    public void addMpiListener(MpiListener listener) {
        if(listener==null) throw new NullPointerException();
        //проверить на наличие такого обработчика, если его нет - добавить
        if(listeners.contains(listener)) return;
        listeners.add(listener);
    }
}
